package ecom.nopecom.util;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.remote.RemoteWebElement;
import org.openqa.selenium.remote.UnreachableBrowserException;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.WebDriverWait;

public class NopeDriver// implements WebDriver
{

	private static final NopeLogger LOGGER = NopComFactory.getLogger(NopeDriver.class);
	private static WebDriver mDriver;

    public NopeDriver() {
        getCurrentDriver();
    }

    public void get(String arg0) {
		get(arg0);

	}

//    public static WebElement findElement(By by)
//    {
//       return mDriver.findElement(by);
//    }

	public String getCurrentUrl() {
		// TODO Auto-generated method stub
		return null;
		
	}

	public String getPageSource() {
		// TODO Auto-generated method stub
		return null;
	}

	public String getTitle() {
		// TODO Auto-generated method stub
		return null;
	}

	public String getWindowHandle() {
		// TODO Auto-generated method stub
		return null;
	}

	public Set<String> getWindowHandles() {
		// TODO Auto-generated method stub
		return null;
	}

	public WebDriver.Options manage() {
		// TODO Auto-generated method stub
		return null;
	}

	public WebDriver.Navigation navigate() {
		// TODO Auto-generated method stub
		return null;
	}


	public WebDriver.TargetLocator switchTo() {
		// TODO Auto-generated method stub
		return null;
	}
	
	 private static long timer = 0;


	    //Search methods
	 
	    public static WebElement findElement(By by) {
	        return get(by);
	    }

	    public WebElement findElement(String id) {
	        return findElement(By.id(id));
	    }

	    public List<WebElement> findElements(By by) {
	        return findElements(by);
	    }


//	    public static WebElement findElementFromList(By by, int index) {
//	        return findElements(by).get(index);
//	    }

//	    public static List<WebElement> findElements(By mainElementBy, By subElementsBy) {
//	        WebElement main = findElement(mainElementBy);
//	        return main.equals(NULL_WEB_ELEMENT) ? NULL_WEB_ELEMENT_LIST : main.findElements(subElementsBy);
//	    }

//	    public static WebElement findElement(By mainElementBy, By subElementsBy) {
//	        WebElement main = findElement(mainElementBy);
//	        return main.equals(NULL_WEB_ELEMENT) ? NULL_WEB_ELEMENT : main.findElement(subElementsBy);
//	    }

	    //Web Element action methods
	    public final static WebElement NULL_WEB_ELEMENT = new RemoteWebElement();
	    public void click(By by) {
	        WebElement element = findElement(by);
	        if (element == NULL_WEB_ELEMENT || element == null) {
	            LOGGER.info("Element with By: " + by + " wasn't clicked!");
	        } else {
	            LOGGER.info("element is not null");
	            element.click();
	        }
	    }

	    public void click(String id) {
	        click(By.id(id));
	    }

	    public static void click(WebElement webElement) {
	        if (webElement != null) webElement.click();
	    }

	    public void clear(By by) {
	        WebElement element = findElement(by);
	        if (element != NULL_WEB_ELEMENT) element.clear();
	    }

	    public void clear(String id) {
	        clear(By.id(id));
	    }

	    public void clear(WebElement element) {
	        if (element != null) element.clear();
	    }

	    public void sendKeys(By by, String value) {
	        WebElement element = findElement(by);
	        if (element != NULL_WEB_ELEMENT) element.sendKeys(value);
	    }

	    public void sendKeys(String id, String value) {
	        sendKeys(By.id(id), value);
	    }

	    public void sendKeys(WebElement element, String value) {
	        if (element != null) element.sendKeys(value);
	    }

	    
	    public final static String NULL_STRING = new String();
	    public final static List<WebElement> NULL_WEB_ELEMENT_LIST = new ArrayList<WebElement>();
	    public final static String GET_OTP_FROM_SERVICE = "otp";
	    public final static String GET_FRAUD_CODE_FROM_LOG = "validCode";
	    public String getElementText(By by) {
	        WebElement element = findElement(by);
	        if (element != NULL_WEB_ELEMENT) return element.getText();
	        return NULL_STRING;
	    }

	    public String getElementText(String id) {
	        return getElementText(By.id(id));
	    }

	    public String getAttribute(String id, String attribute) {
	        return getAttribute(By.id(id), attribute);
	    }

	    public String getAttribute(By by, String attribute) {
	        WebElement element = findElement(by);
	        return element.equals(NULL_WEB_ELEMENT) ? NULL_STRING : element.getAttribute(attribute);
	    }

	    public String getAttribute(WebElement element, String attribute) {
	        if (element == null) {
	            return NULL_STRING;
	        }
	        LOGGER.info("WebElement [" + attribute + "] had value [" + element.getAttribute(attribute) + "]");
	        return element.getAttribute(attribute) == (null) ? NULL_STRING : element.getAttribute(attribute);

	    }

	    public boolean verifyAttributeValue(By by, String attribute, String expected) {
	        return getAttribute(by, attribute).equals(expected);
	    }

	    public boolean verifyAttributeValue(String id, String attribute, String expected) {
	        return getAttribute(id, attribute).equals(expected);
	    }

	    public boolean verifyAttributeValue(WebElement webElement, String attribute, String expected) {
	        return getAttribute(webElement, attribute).equals(expected);
	    }

	    public String getCssValue(By by, String cssValue) {
	        WebElement element = findElement(by);
	        return element.equals(NULL_WEB_ELEMENT) ? NULL_STRING : element.getCssValue(cssValue);
	    }

	    public String getCssValue(String id, String cssValue) {
	        return getCssValue(By.id(id), cssValue);
	    }

	    public String getTagName(By by) {
	        WebElement element = findElement(by);
	        return element.equals(NULL_WEB_ELEMENT) ? NULL_STRING : element.getTagName();
	    }

	    public String getTagName(String id) {
	        return getTagName(By.id(id));
	    }

	    public boolean verifyElementText(By by, String expected) {
	        return getElementText(by).equals(expected);
	    }

	    public boolean verifyElementText(String id, String expected) {
	        return verifyElementText(By.id(id), expected);
	    }

	    public List<WebElement> getDropDownOptions(WebElement webElement) {
	        Select select = new Select(webElement);
	        return select.getOptions();
	    }

	    public WebElement getDropDownOption(By by, String value) {
	        List<WebElement> options = getDropDownOptions(findElement(by));
	        for (WebElement element : options) {
	            if (element.getAttribute("value").equalsIgnoreCase(value)) {
	                return element;
	            }
	        }
	        return NULL_WEB_ELEMENT;
	    }
public WebElement getDropDownOption(WebElement webElement, String value) {
	        List<WebElement> options = getDropDownOptions(webElement);
	        for (WebElement element : options) {
	            if (element.getAttribute("value").equalsIgnoreCase(value)) {
	                return element;
	            }
	        }
	        return NULL_WEB_ELEMENT;
	    }


	    public void clickDropDownOption(String id, String value) {
	        WebElement element = getDropDownOption(By.id(id), value);
	        if (element != NULL_WEB_ELEMENT) {
	            element.click();
	        } else {
	            LOGGER.error("Element: " + id + " not clicked");
	        }
	    }

	    public void clickDropDownOption(By by, String value) {
	        WebElement element = getDropDownOption(by, value);
	        if (element != NULL_WEB_ELEMENT) {
	            element.click();
	        } else {
	            LOGGER.error("Element: " + by + " not clicked");
	        }
	    }

	    public void clickDropDownOption(WebElement webelement, String value) {
	        WebElement element = getDropDownOption(webelement, value);
	        if (element != NULL_WEB_ELEMENT) {
	            element.click();
	        } else {
	            LOGGER.error("Element: " + webelement.getTagName() + " not clicked");
	        }
	    }

	    public List<WebElement> getDropDownOptions(By by) {
	        return getDropDownOptions(findElement(by));
	    }

	    public List<WebElement> getDropDownOptions(String id) {
	        return getDropDownOptions(findElement(id));
	    }

	    public boolean isDisplayed(By by) {
	        return findElement(by).isDisplayed();
	    }

	    public boolean isDisplayed(String id) {
	        return isDisplayed(By.id(id));
	    }

	    public boolean isDisplayed(WebElement webelement) {
	        return webelement.isDisplayed();
	    }

	    public boolean isSelected(By by) {
	        return findElement(by).isSelected();
	    }

	    public boolean isSelected(String id) {
	        return isSelected(By.id(id));
	    }

	    public static boolean elementExists(By by) {
	        return findElement(by) != NULL_WEB_ELEMENT;
	    }

	    public static boolean elementExists(String pId) {
	        return elementExists(By.id(pId));
	    }

	    public static Boolean elementExists(WebElement webElement) {
	        return elementExists(By.id(webElement.getAttribute("id")));
	    }
	    public static WebElement waitForElement(WebElement elementToWaitFor, Integer waitTimeInSeconds) {
	        WebElement element = null;

	        if (waitTimeInSeconds == null) {
	            waitTimeInSeconds = DEFAULT_WAIT_SECONDS;
	        }

	        try {
	            element = (new WebDriverWait(getCurrentDriver(), waitTimeInSeconds)).until(
	                    ExpectedConditions.visibilityOf(elementToWaitFor));
	        } catch (NoSuchElementException ex) {
	            LOGGER.warn("NoSuchElementException: Could not find element after waiting for " + waitTimeInSeconds + " secs");
	        } catch (TimeoutException tex) {
	            LOGGER.warn("TimeoutException: Could not find element after waiting for " + waitTimeInSeconds + " secs");
	        }

	        return element;
	    }
	    private static boolean driverNotSet() {
	        return mDriver == null || mDriver.getWindowHandle() == null;
	    }
	    public static WebDriver getCurrentDriver() {
	        try {
	            if (driverNotSet()) {
	                LOGGER.info("mDriver is null - get new webdriver");
	                mDriver = NopComFactory.getBrowser();
	               // Runtime.getRuntime().addShutdownHook(new Thread(new BrowserCleanup()));
	            }
	        } catch (UnreachableBrowserException e) {
	            LOGGER.error("get new webdriver, UnreachableBrowserException thrown");
//	            e.printStackTrace();
	            mDriver = NopComFactory.getBrowser();
	        } catch (WebDriverException e) {
	            LOGGER.error("get new webdriver, WebDriverException thrown");
//	            e.printStackTrace();
	            mDriver = NopComFactory.getBrowser();
	        }
	        return mDriver;
	    }
	    public static WebElement waitForElement(WebElement elementToWaitFor) {
	        return waitForElement(elementToWaitFor, null);
	    }

	    public WebElement waitForElement(String elementToWaitForByCSS) {
	        return waitForElement(elementToWaitForByCSS, null);
	    }

//	    public void waitForTransitionsToComplete() {
//	        waitForTransitionsToComplete(10000);
//	    }

//	    public void waitForTransitionsToComplete(int waitTimeMilliseconds) {
//	        ProcessTimer processTimer = ProcessTimer.start();
//	        Condition elementIsDisplayed = new ElementIsDisplayed(By.id("ve_done"));
//	        Poller.start().withTimeout(waitTimeMilliseconds).pollingEvery(200).pollUntilConditionIsSatisfied(elementIsDisplayed);
//	        LOGGER.info("Transitions completed in " + processTimer.executionTime());
//	    }
	    public static final int FIVE_SECONDS = 5;
	    public static final int TEN_SECONDS = 10;
	    public static final int TWENTY_SECONDS = 20;
	    public static final int DEFAULT_WAIT_SECONDS = TEN_SECONDS;
	    /**
//	     * Selenium alternative to existing {@link (WebElement)}
	     * Method takes CSS path in as parameter
	     *
	     * @param elementToWaitForByCSS WebElement
	     * @return WebElement Element that was found
	     */
	    public WebElement waitForElement(String elementToWaitForByCSS, Integer waitTimeInSeconds) {
	        WebElement element = null;

	        if (waitTimeInSeconds == null) {
	            waitTimeInSeconds = DEFAULT_WAIT_SECONDS;
	        }

	        try {
	            element = (new WebDriverWait(getCurrentDriver(), waitTimeInSeconds)).until(
	                    ExpectedConditions.visibilityOfElementLocated(By.cssSelector(elementToWaitForByCSS)));
	        } catch (NoSuchElementException ex) {
	            LOGGER.debug("Could not find element via CSS path [" + elementToWaitForByCSS + " " + "after waiting for " + waitTimeInSeconds);
	        }

	        return element;
	    }

	    public static void waitFor(long millis) {
	        pauseSearchFor(millis);
	    }

	    public void press(Keys key) {
	        new Actions(getCurrentDriver()).keyDown(key).keyUp(key).perform();
	    }

	    public String getPageHTML() {
	        return getCurrentDriver().getPageSource();
	    }


	    public void selectFrame(String pId) {
	        getCurrentDriver().switchTo().frame(pId);
	    }

	    public void close() {
	        try {
	            if (driverSet()) {
	                getCurrentDriver().quit();
	                mDriver = null;
	                LOGGER.info("~~~~~~~~~  browser closed ~~~~~~~~~");
	            } else {
	                LOGGER.info("~~~~~~~~~  browser was not open ~~~~~~~~~");
	            }
	        } catch (UnreachableBrowserException e) {

	            LOGGER.info("CANNOT QUIT BROWSER. UnreachableBrowserException thrown");
	        }
	    }


//	    public void deleteAllCookies() {
//	        LOGGER.info("deleteAllCookies()");
//	        deleteAllCookies(null);
//	    }

//	    public void deleteAllCookies(String url) {
//	        LOGGER.info("deleteAllCookies(url)");
//
//	        if (url != null) {
//	            LOGGER.info("URL to delete cookies: " + url);
//	            loadPage(url);
//	        }
//
//	        Set<Cookie> allCookies = getCurrentDriver().manage().getCookies();
//	        if (allCookies != null) {
//	            for (Cookie cookie : allCookies) {
//	                LOGGER.info("Cookie name before : " + cookie.getName() + " Cookie value: " + cookie.getValue());
//	            }
//	            getCurrentDriver().manage().deleteAllCookies();
//
//	            allCookies = getCurrentDriver().manage().getCookies();
//	            for (Cookie cookie : allCookies) {
//	                LOGGER.info("Cookie name after : " + cookie.getName() + " Cookie value: " + cookie.getValue());
//	            }
//	        } else {
//	            LOGGER.info("~~~~~~~~ NO COOKIES ~~~~~~~~~~");
//	        }
//	    }

	    public void setCookie(String domainUrl, Cookie... cookie) {
	       // loadPage(domainUrl);
	        for (Cookie cookieToAdd : cookie) {
	            getCurrentDriver().manage().addCookie(cookieToAdd);
	        }
	    }

	    public void setCookie(Cookie cookie, String domainUrl) {
	        setCookie(domainUrl, cookie);
	    }
	    public WebElement getDivByText(String divText) {
	        String xPath = "//div[text()='" + divText + "']";
	        return findElement(By.xpath(xPath));
	    }

	    public static void pauseSearchFor(long millis) {
	        try {
	            timer = timer + millis;
	            LOGGER.info("Pausing JZDriver for " + millis + " ms");
	            Thread.sleep(millis);
	        } catch (InterruptedException e) {
	            throw new RuntimeException(e);
	        }
	    }
	    public void triggerBlurById(String elementId) {
	        String jsString = "$('#" + elementId + "').blur();";
	        NopeDriver.executeJS(jsString);
	    }
	    /**
	     * Method to invoke a blur action on an element.  You can only use this method
	     * if the WebElement you pass it has an ID for JQuery to use.
	     *
	     * @param element
	     */
	    public void triggerBlurOnWebElementUsingId(WebElement element) {
	        if (element.getAttribute("id") != null) {
	            triggerBlurById(element.getAttribute("id"));
	        } else {
	            LOGGER.warn("This WebElement does not have an ID so you can't use this method");
	        }

	    }

	    public static String getXPath(WebElement element) {
	        String jscript = "function getPathTo(node){" +
	                " var stack = [];" +
	                " while(node.parentNode !== null) {" +
	                "  stack.unshift(node.tagName + '[id=' + node.id + ',class=' + node.className + ']');" +
	                "  node = node.parentNode;" +
	                " }" +
	                " return stack.join('/');" +
	                "}" +
	                "return getPathTo(arguments[0]);";
	        return (String) ((JavascriptExecutor) mDriver).executeScript(jscript, element);
	    }

	    /**
	     * For some reason, getting children using By.CssSelector is very slow with WebElement... this is so much faster
	     */
	    public static List<WebElement> getChildren(WebElement element) {
	        List<Object> children = (List<Object>) ((JavascriptExecutor) mDriver).executeScript("return arguments[0].childNodes", element);
	        List<WebElement> elements = new ArrayList<WebElement>();
	        for (Object child : children) {
	            if (child instanceof WebElement) {
	                elements.add((WebElement) child);
	            }
	        }
	        return elements;
	    }

	    public static WebElement getParent(WebElement element) {
	        return element.findElement(By.xpath(".."));
	    }

	    /**
	     * Get a node's previous node (equivalent to DOM's "previousSibling" attribute)
	     */
	    public static WebElement getPreviousSibling(WebElement element) {
	        Object response = ((JavascriptExecutor) mDriver).executeScript("return arguments[0].previousSibling", element);
	        if (response instanceof WebElement) {
	            return (WebElement) response;
	        } else {
	            return null;
	        }
	    }

	    public static void executeJS(String jsCode) {
	        JavascriptExecutor js = (JavascriptExecutor) mDriver;
	        js.executeScript(jsCode);
	    }

	    /**
	     * Method used to establish if a particular element is currently displayed.  This
	     * method makes use of {@link #waitForElement(WebElement)} to determine if the
	     * element existed before determining if its actually displayed
	     *
	     * @param element WebElement
	     * @return boolean
	     */
//	    public static boolean isDisplayedCheck(WebElement element) {
//	        if (element != null) {
//	            WebElement pageDiv = nopeDriver.waitForElement(element);
//	            if (pageDiv == null) {
//	                return false;
//	            }
//	            return pageDiv.isDisplayed();
//	        } else {
//	            LOGGER.info("Unable to determine if element is displayed as element was null");
//	            return false;
//	        }
//	    }

	    public boolean isNotDisplayedCheck(WebElement element) {
	        int numberOfIterations = TWENTY_SECONDS * 5;
	        //if element is deleted return true (i.e catch noSuchElementException)
	        try {
	            for (int i = 0; i < numberOfIterations; i++) {
	                if (element.isDisplayed()) {
	                    waitFor(200);
	                } else {
	                    return true;
	                }
	            }
	            return false;
	        } catch (NoSuchElementException e) {
	            return true;
	        } catch (StaleElementReferenceException e) {
	            return true;
	        }
	    }

	    public static void clickDropDownOptionByText(WebElement webelement, String text) {
	        try {
	            (new Select(webelement)).selectByVisibleText(text);
	        } catch (NoSuchElementException nse) {
	            LOGGER.error("Given option " + text + " not exists");
	            throw new NoSuchElementException(text);
	        }

	    }

	    public static String getSelectedValueInDropdown(WebElement webElement) {
	        String selectedOption = null;
	        try {
	            selectedOption =  (new Select(webElement)).getFirstSelectedOption().getText();
	        } catch (NoSuchElementException nse) {
	            LOGGER.error("Cant find selected option");
	        }

	        return  selectedOption;
	    }

	    public Boolean elementExistsByClass(WebElement webElement) {
	        try {

	            return elementExists(By.className(webElement.getAttribute("class")));

	        } catch (NoSuchElementException nse) {
	            LOGGER.info("Element not found");
	        }
	        return false;
	    }

	    public void hoverOver(WebElement webElement) {
	        Actions actions = new Actions(getCurrentDriver());
	        actions.moveToElement(webElement).build().perform();
	    //    actions.moveByOffset(1, 1).build().perform();

	    }

//	    private static class BrowserCleanup implements Runnable {
//
//	        public void run() {
//	            if (driverSet()) {
//	                LOGGER.info("Closing remaining browser");
//	                close();
//	            }
//	        }
//	    }

	    public boolean driverSet()
	    {
	    	return !driverNotSet();
	    }
	    public static void getTimeWaited() {

	        LOGGER.info("Total time waited " + timer);

	    }

	    public void quit() {
	        mDriver.close();
	    }

	    private static void reloadBrowser() {
	        boolean retry = true;
	        int attempts = 3;
	        while (retry || attempts != 0) {
	            try {
	                getCurrentDriver();
	                retry = false;
	                attempts = 0;
	            } catch (UnreachableBrowserException e) {
	                attempts--;
	            }
	        }
	    }
	    private static WebElement get(By by) {
	        Timer timer = new Timer(10);
	        while (!timer.hasExpired()) {
	            try {

	                reloadBrowser();
	                WebElement element = mDriver.findElement(by);
	                if (element == null) continue;
	                if (element.isDisplayed()) {
	                    return element;
	                }
	            } catch (NoSuchElementException e) {
	                LOGGER.info("NoSuchElementException thrown, now waiting");
	                waitFor(500);
	            }
	        }

	        return NULL_WEB_ELEMENT;
	    }


    public static boolean isElementAndTextPresent(By errorElement, String text) {

            return  (elementExists(errorElement)) && (NopeDriver.findElement(errorElement).getText().contentEquals(text));
    }
}
